/*
 * MyCamera.cpp
 *
 *  Created on: 15 Oct 2013
 *      Author: Patrick
 */

#include "MyCamera.h"

using namespace Ogre;

MyCamera::MyCamera(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* renderWnd,
		MyInputManager* inptMgr) {

	this->renderWindow = renderWnd;
	this->sceneManager = sceneMgr;

	inptMgr->registerKeyListener(this);
	inptMgr->registerMouseListener(this);

	this->moveSpeed = 0.1f;
	this->rotateSpeed = 0.03f;

	this->targetNodePosition = Vector3(0, 0, 0);
	this->targetFrontDirection = Vector3(0, 1, 0);
	this->targetUpDirection = Vector3(0, 0, 1);
	this->targetSideDirection = Vector3(-1, 0, 0);

	this->targetToCameraHorizontalDirection = Vector3(0, -1, 0);
	this->targetToCameraVerticalDirection = Vector3(0, 0, 1);
	this->targetToCameraDistance = 30000;

	this->moveScale = 0.0;
	this->rotScale = 0.0;
	this->scrolling = 0;

	this->movingForward = false;
	this->movingBackwards = false;
	this->movingToTheLeft = false;
	this->movingToTheRight = false;
	this->turningToTheLeft = false;
	this->turningToTheRight = false;
	this->movingCamera = false;

	camera = sceneMgr->createCamera("Camera");
	sceneMgr->getRootSceneNode()->attachObject(camera);

	Vector3 cameraOffset = this->targetToCameraHorizontalDirection
			* this->targetToCameraDistance
			+ this->targetToCameraVerticalDirection
					* this->targetToCameraDistance;

	Vector3 cameraPosition = this->targetNodePosition + cameraOffset;

	this->camera->setPosition(cameraPosition);
	this->camera->setDirection(-1 * cameraOffset);
	this->camera->setNearClipDistance(1);
	this->camera->setFarClipDistance(0);
	this->camera->setFixedYawAxis(true, Vector3::UNIT_Z);

	//****************************************************//
	// creates the viewport and sets the background color //
	//****************************************************//
	viewport = renderWindow->addViewport(camera);
	viewport->setBackgroundColour(ColourValue(0.0f, 0.0f, 0.0f, 1.0f));

	camera->setAspectRatio(
			Real(viewport->getActualWidth())
					/ Real(viewport->getActualHeight()));

	viewport->setCamera(camera);

	//*****************************************************
}

MyCamera::~MyCamera() {

}

void MyCamera::updateCamera(double timeSinceLastFrame) {
	moveScale = moveSpeed * (float) timeSinceLastFrame
			* (this->targetToCameraDistance / 200);
	rotScale = rotateSpeed * (float) timeSinceLastFrame;

	//**********//
	// rotation //
	//**********//

	if (this->turningToTheLeft) {
		Quaternion rotation = Quaternion(Degree(-rotScale), Vector3::UNIT_Z);

		this->targetToCameraHorizontalDirection = rotation
				* this->targetToCameraHorizontalDirection;

		this->targetFrontDirection = rotation * this->targetFrontDirection;
		this->targetSideDirection = this->targetUpDirection.crossProduct(
				this->targetFrontDirection);
	}
	if (this->turningToTheRight) {
		Quaternion rotation = Quaternion(Degree(rotScale), Vector3::UNIT_Z);

		this->targetToCameraHorizontalDirection = rotation
				* this->targetToCameraHorizontalDirection;

		this->targetFrontDirection = rotation * this->targetFrontDirection;
		this->targetSideDirection = this->targetUpDirection.crossProduct(
				this->targetFrontDirection);
	}

	//**********//
	// movement //
	//**********//
	Vector3 movement = Vector3::ZERO;

	if (this->movingForward)
		movement += this->targetFrontDirection;
	if (this->movingBackwards)
		movement -= this->targetFrontDirection;
	if (this->movingToTheLeft)
		movement += this->targetSideDirection;
	if (this->movingToTheRight)
		movement -= this->targetSideDirection;

	//*************//
	// Target Node //
	//*************//

	movement.normalise();
	movement *= this->moveScale;

	//move the target node
	this->targetNodePosition += movement;

	//*************//
	// Camera Node //
	//*************//

	//move the camera node
	Vector3 cameraOffset = this->targetToCameraHorizontalDirection
			* this->targetToCameraDistance
			+ this->targetToCameraVerticalDirection
					* this->targetToCameraDistance;
	this->camera->setPosition(this->targetNodePosition + cameraOffset);
	this->camera->setDirection(-1 * cameraOffset);
}

bool MyCamera::mouseMoved(const OIS::MouseEvent &evt) {

	if (evt.state.Z.rel != 0) {
		this->targetToCameraDistance += evt.state.Z.rel * 5;

		if(this->targetToCameraDistance < 100)
			this->targetToCameraDistance = 100;
	}

	return true;
}

bool MyCamera::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id) {

	if (id == 2)
		this->movingCamera = true;

	return true;
}

bool MyCamera::mouseReleased(const OIS::MouseEvent &evt,
		OIS::MouseButtonID id) {

	if (id == 2)
		this->movingCamera = false;

	return true;
}

bool MyCamera::keyPressed(const OIS::KeyEvent &evt) {

	switch (evt.key) {
	case OIS::KC_A:
		this->movingToTheLeft = true;
		break;
	case OIS::KC_D:
		this->movingToTheRight = true;
		break;
	case OIS::KC_W:
		this->movingForward = true;
		break;
	case OIS::KC_S:
		this->movingBackwards = true;
		break;
	case OIS::KC_Q:
		this->turningToTheLeft = true;
		break;
	case OIS::KC_E:
		this->turningToTheRight = true;
		break;
	case OIS::KC_SYSRQ:
		this->renderWindow->writeContentsToTimestampedFile("BOF_Screenshot_",
				".png");
		break;
	default:
		break;
	}

	return true;
}

bool MyCamera::keyReleased(const OIS::KeyEvent &evt) {
	switch (evt.key) {
	case OIS::KC_A:
		this->movingToTheLeft = false;
		break;
	case OIS::KC_D:
		this->movingToTheRight = false;
		break;
	case OIS::KC_W:
		this->movingForward = false;
		break;
	case OIS::KC_S:
		this->movingBackwards = false;
		break;
	case OIS::KC_Q:
		this->turningToTheLeft = false;
		break;
	case OIS::KC_E:
		this->turningToTheRight = false;
		break;
	default:
		break;
	}

	return true;
}
